package org.jvm.instruction.references.invokemethod;

import org.jvm.instruction.base.*;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.*;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.rtda.heap.symref.InterfaceMethodRef;
import org.jvm.rtda.thread.Frame;

/**
 * 接口类实例调用
 *
 * @author 海燕
 * @date 2023/2/7
 */
public class INVOKE_INTERFACE implements Instruction {

    private int index;

    @Override
    public void fetchOperands(ByteCodeReader reader) {
        this.index = reader.readUint16();
        //两个兼容字段也需要读一下
        reader.readUint8();
        reader.readUint8();
    }

    @Override
    public void execute(Frame frame) {
        InterfaceMethodRef methodRef = (InterfaceMethodRef) frame.getMethod().getKlass().getConstantPool().getConstant(this.index);
        //被调用方字面类型
        Klass resolvedClass = methodRef.resolvedClass(frame.getThread());
        //实际将要执行的方法,注意是接口方法，没有方法体
        Method resolvedMethod = methodRef.resolveMethodRef(frame.getThread());
        //被调用方法实际所在类
        Klass infactClass = resolvedMethod.getKlass();
        //调用方，指令实际执行所在类
        Klass currentClass = frame.getMethod().getKlass();

        //实例方法不应是静态
        if (resolvedMethod.isStatic() || resolvedMethod.isPrivate()) {
            throw new RuntimeException("method is static or private");
        }
        //获取实例引用
        Object ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
        if (ref == null) {
            frame.getThread().throwNullPointerException();
            return;
        }
        //实例应当实现了接口
        if (!ref.getKlass().isImplements(resolvedClass)) {
            throw new RuntimeException("instance not implements interface");
        }
        //从实例中查找实际要执行的方法
        Method methodToBeInvoked = MethodUtil.lookupMethodInClass(ref.getKlass(), methodRef.getName(), methodRef.getDescriptor());
        //不能是抽象方法
        if (methodToBeInvoked == null || methodToBeInvoked.isAbstract() || !methodToBeInvoked.isPublic()) {
            throw new RuntimeException("method is abstract or null or not public");
        }
        //开始方法调用
        InstructionUtil.invokeMethod(frame, methodToBeInvoked);
    }
}
